package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.VendoutContract;
import com.lkd.contract.VendoutResultContract;
import com.lkd.dao.ChannelDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.ChannelEntity;
import com.lkd.entity.SkuEntity;
import com.lkd.entity.VendoutRunningEntity;
import com.lkd.exception.LogicException;
import com.lkd.http.vo.VMChannelConfig;
import com.lkd.service.ChannelService;
import com.lkd.service.SkuService;
import com.lkd.service.VendoutRunningService;
import com.lkd.vo.Pager;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
@Slf4j
public class ChannelServiceImpl extends ServiceImpl<ChannelDao, ChannelEntity> implements ChannelService {
    @Resource
    private VendoutRunningService runningService;
    @Resource
    private MqttProducer mqttProducer;
    @Resource
    private RedissonClient redissonClient;
    @Override
    public Pager<ChannelEntity> findPage(long pageIndex, long pageSize, Map searchMap) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<ChannelEntity> page =
                new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageIndex, pageSize);

        QueryWrapper queryWrapper = createQueryWrapper(searchMap);
        this.page(page, queryWrapper);

        Pager<ChannelEntity> pageResult = new Pager<>();
        pageResult.setCurrentPageRecords(page.getRecords());
        pageResult.setPageIndex(page.getCurrent());
        pageResult.setPageSize(page.getSize());
        pageResult.setTotalCount(page.getTotal());
        return pageResult;
    }

    @Override
    public List<ChannelEntity> getChannelesByInnerCode(String innerCode) {
        LambdaQueryWrapper<ChannelEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ChannelEntity::getInnerCode, innerCode);

        return this.list(queryWrapper);
    }

    @Override
    public ChannelEntity getChannelInfo(String innerCode, String channelCode) {
        LambdaQueryWrapper<ChannelEntity> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(ChannelEntity::getInnerCode, innerCode)
                .eq(ChannelEntity::getChannelCode, channelCode);
        return this.getOne(queryWrapper);
    }

    @Override
    public List<ChannelEntity> findList(Map searchMap) {
        QueryWrapper queryWrapper = createQueryWrapper(searchMap);
        return this.list(queryWrapper);
    }

    /**
     * 条件构建
     *
     * @param searchMap
     * @return
     */
    private QueryWrapper createQueryWrapper(Map searchMap) {
        QueryWrapper queryWrapper = new QueryWrapper();
        if (searchMap != null) {
            queryWrapper.allEq(searchMap);
        }
        return queryWrapper;
    }

    @Autowired
    private SkuService skuService;

    /**
     * 设置商品
     *
     * @param innerCode
     * @param channelCode
     * @param skuId
     * @return
     */
    private boolean configSku(String innerCode, String channelCode, long skuId) {

        if (skuId <= 0) {
            return false;
        }
        SkuEntity skuEntity = skuService.getById(skuId); //查询商品
        if (skuEntity == null) {
            return false;
        }

        ChannelEntity channel = this.getChannelInfo(innerCode, channelCode);
        if (channel == null) {
            throw new LogicException("该货道不存在");
        }
        channel.setSkuId(skuId);
        channel.setPrice(skuEntity.getPrice());
        return this.updateById(channel);
    }


    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Boolean mapSku(VMChannelConfig channelConfig) {
        channelConfig.getChannelList().forEach(c ->
                configSku(channelConfig.getInnerCode(), c.getChannelCode(), Long.valueOf(c.getSkuId())));
        return true;
    }

    /**
     * 更新库存
     *
     * @param contract 出货请求协议
     */
    @Override
    @Transactional
    public void updateStock(VendoutContract contract) {
        try {
            String innerCode = contract.getInnerCode();
            //货道编号
            String channelCode = contract.getVendoutData().getChannelCode();
            long skuId = contract.getVendoutData().getSkuId();
            //1.查询货道列表
            List<ChannelEntity> channeles = getChannelesByInnerCode(innerCode);
            //2.查询商品所在的货道   可能有多个货道卖的相同的商品,拿一个就行
            Optional<ChannelEntity> channel = channeles.stream()
                    //判断当前货道是否还有余量
                    .filter(c -> c.getCurrentCapacity() > 0)
                    //找到对应商品
                    .filter(c -> c.getSkuId() == skuId)
                    //拿到第一个,出货
                    .findFirst();
            //3.修改库存操作
            if (channel.isPresent()) {
                //存在,则修改库存
                ChannelEntity channelEntity = channel.get();
                channelEntity.setCurrentCapacity(channelEntity.getCurrentCapacity() - 1);
                //保存更新货道数据,库存表
                updateById(channelEntity);
                //解锁,
                RLock lock = redissonClient.getLock(String.format(VMSystem.VM_LOCK_KEY_PREF, contract.getInnerCode()));
                lock.unlock();
                //4.存入出货流水数据
                VendoutRunningEntity running = new VendoutRunningEntity();
                running.setOrderNo(contract.getVendoutData().getOrderNo());
                running.setInnerCode(innerCode);
                //status 要为false，要售货机终端上报结果后才改为true
                running.setStatus(false);
                running.setSkuId(skuId);
                running.setCreateTime(LocalDateTime.now());
                running.setUpdateTime(LocalDateTime.now());
                //保存更新流水表
                runningService.updateById(running);

                //5.下发到售货机终端   货道编号
                contract.getVendoutData().setChannelCode(channelCode);
                //将货道编号记录到发货协议,发送消息到MQ
                mqttProducer.send(TopicConfig.getVendoutTopic(contract.getInnerCode()),2,contract);
            }else {
                log.error("设备缺货中,无法出货");
            }
        } catch (JsonProcessingException e) {
            log.info("出货异常"+e);
        }
    }
}
